package org.jflame.web;

import org.jflame.commons.file.FileHelper;
import org.jflame.commons.file.MimeTypes;
import org.jflame.commons.json.JsonHelper;
import org.jflame.commons.model.pair.NameValuePair;
import org.jflame.commons.net.IPAddressHelper;
import org.jflame.commons.util.IOHelper;
import org.jflame.commons.util.StringHelper;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.apache.commons.lang3.ArrayUtils;

import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * web环境常用常量定义和工具方法
 *
 * @author charles.zhang
 */
public class WebUtils {

    /**
     * ajax请求头标识
     */
    public final static NameValuePair AJAX_REQUEST_FLAG = new NameValuePair("x-requested-with", "XMLHttpRequest");

    /**
     * web静态资源扩展名
     */
    public final static String[] WEB_STATIC_EXTS = ArrayUtils.addAll(MimeTypes.WEB_IMAGE_EXTENSIONS, "js", "css", "ttf",
            "font", "woff", "woff2");

    /**
     * 文件下载设置http header
     *
     * @param response HttpServletResponse
     * @param fileName 下载显示文件名
     * @param fileSize 文件大小
     */
    public static void setFileDownloadHeader(HttpServletResponse response, String fileName, Long fileSize) {
        // String encodedfileName = CharsetHelper.reEncodeGBK(fileName);
        // response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName + "\"");
        // 换个新玩法,上面的也是正常的
        try {
            response.setHeader("Content-Disposition",
                    "attachment; filename*=utf-8''" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setContentType(MimeTypes.MIMETYPE_OCTET_STREAM);
        if (fileSize != null && fileSize > 0) {
            response.setHeader("Content-Length", String.valueOf(fileSize));
        }
    }

    public static void setFileDownloadHeader(HttpServletResponse response, File downloadFile) {
        setFileDownloadHeader(response, downloadFile.getName(), downloadFile.length());
    }

    /**
     * 设置客户端缓存过期时间 的Header.
     *
     * @param response HttpServletResponse
     * @param expiresSeconds 过期时间,秒
     */
    public static void setExpiresHeader(HttpServletResponse response, long expiresSeconds) {
        response.setDateHeader("Expires", System.currentTimeMillis() + expiresSeconds * 1000);
        response.setHeader("Cache-Control", "max-age=" + expiresSeconds);
    }

    /**
     * 设置禁止客户端缓存的Header.
     *
     * @param response HttpServletResponse
     */
    public static void setDisableCacheHeader(HttpServletResponse response) {
        response.setDateHeader("Expires", 1L);
        response.addHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache, no-store, max-age=0");
    }

    /**
     * 判断请求是否是一个ajax请求
     * <p>
     * 请求头含x-requested-with=XMLHttpRequest
     *
     * @param request HttpServletRequest
     * @return true=是ajax请求
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        if (AJAX_REQUEST_FLAG.value()
                .equalsIgnoreCase(request.getHeader(AJAX_REQUEST_FLAG.name()))) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否请求json格式数据,header accept带json或请求路径以json结尾
     *
     * @param request
     * @return true=是json请求
     */
    public static boolean isJsonRequest(HttpServletRequest request) {
        String headAccept = request.getContentType();
        boolean yes = false;
        if (headAccept != null && headAccept.indexOf("/json") > 0) {
            yes = true;
        } else if (request.getServletPath()
                .endsWith(".json")) {
            yes = true;
        }
        return yes;
    }

    public static void outJson(HttpServletResponse response, Object entity) throws IOException {
        outJson(response, entity, null);
    }

    /**
     * 对象转为json后输出到到response.
     *
     * @param response HttpServletResponse
     * @param entity 待序列化对象
     * @param charset 字符集,为null时采用utf-8
     * @throws IOException
     */
    public static void outJson(HttpServletResponse response, Object entity, Charset charset) throws IOException {
        setDisableCacheHeader(response);
        if (charset == null) {
            response.setContentType(MimeTypes.MIME_TYPE_JSON_UTF8);
        } else {
            response.setContentType(MimeTypes.MIME_TYPE_JSON + ";charset=" + charset.name());
        }
        PrintWriter out = response.getWriter();
        if (entity instanceof String) {
            out.print(entity);
        } else {
            out.print(JsonHelper.toJson(entity));
        }
    }

    /**
     * 读取http请求body并转字符串返回
     *
     * @param request http请求
     * @return http请求体字符串
     * @throws IOException 读取异常
     */
    public static String readBody(HttpServletRequest request) throws IOException {
        String charsetName = request.getCharacterEncoding();
        Charset charset = charsetName == null ? StandardCharsets.UTF_8 : Charset.forName(charsetName);
        ServletInputStream inputStream = request.getInputStream();
        return IOHelper.readText(inputStream, charset);
    }

    /**
     * 获取应用的绝对url
     * <p>
     * 如:http://www.xx.com:8080/webapp,https://www.xx.com
     *
     * @param request HttpServletRequest
     * @return
     */
    public static String getApplicationPath(HttpServletRequest request) {
        String tmpPath = request.getScheme() + "://" + request.getServerName();
        // 80,443不显示端口号
        if (request.getServerPort() != 80 && !(request.getServerPort() == 443 && "https".equals(request.getScheme()))) {
            tmpPath = tmpPath + ":" + request.getServerPort();
        }
        return tmpPath + request.getContextPath();
    }

    /**
     * 返回请求路径,不包含应用路径和查询参数
     *
     * @param request HttpServletRequest
     * @return
     */
    public static String getRequestPath(HttpServletRequest request) {
        String url = request.getServletPath();
        if (request.getPathInfo() != null) {
            url += request.getPathInfo();
        }
        return url;
    }

    /**
     * 获取客户端ip地址
     *
     * @param request HttpServletRequest
     * @return
     */
    public static String getRemoteClientIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (isDisabledIp(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (isDisabledIp(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (isDisabledIp(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (isDisabledIp(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (isDisabledIp(ip)) {
            ip = request.getRemoteAddr();
        }
        // 多个ip取第一个
        if (ip.indexOf(",") >= 0) {
            String[] ips = ip.split(",");
            for (String addr : ips) {
                if (!isDisabledIp(addr)) {
                    ip = addr;
                    break;
                }
            }
        }
        ip = ip.trim();
        if (ArrayUtils.contains(IPAddressHelper.LOOPBACK_IPS, ip)) {
            ip = IPAddressHelper.getLANIPString();
        }
        return ip;
    }

    private static boolean isDisabledIp(String ip) {
        return StringHelper.isEmpty(ip) || ArrayUtils.contains(IPAddressHelper.LOOPBACK_IPS, ip)
                || "unknown".equalsIgnoreCase(ip);
    }

    /**
     * 获取指定名称的cookie值
     *
     * @param request HttpServletRequest
     * @param cookieName cookie名
     * @return cookie字符串值，不存在返回null
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                if (c.getName()
                        .equals(cookieName)) {
                    return c.getValue();
                }
            }
        }
        return null;
    }

    /**
     * 设置cookie
     *
     * @param response HttpServletResponse
     * @param cookieName cookie名
     * @param cookieValue cookie值
     */
    public static void addCooke(HttpServletResponse response, String cookieName, String cookieValue) {
        response.addCookie(new Cookie(cookieName, cookieValue));
    }

    /**
     * 设置cookie，并设置生存时间
     *
     * @param response HttpServletResponse
     * @param cookieName cookie名
     * @param cookieValue cookie值
     * @param maxAge 生存时间,单位秒，0将删除该cookie
     */
    public static void addCooke(HttpServletResponse response, String cookieName, String cookieValue, int maxAge) {
        Cookie cookie = new Cookie(cookieName, cookieValue);
        cookie.setMaxAge(maxAge);
        response.addCookie(cookie);
    }

    /**
     * 判断请求是否是一个web静态资源文件(如图片,字体,js,css等),基于扩展名判断
     *
     * @param request
     * @return
     */
    public static boolean isWebResource(HttpServletRequest request) {
        String requestUrl = getRequestPath(request);
        return isWebResource(requestUrl);
    }

    /**
     * 判断请求地址是否是一个web静态资源文件(如图片,字体,js,css等),基于扩展名判断
     *
     * @param requestUrl 请求地址
     * @return
     */
    public static boolean isWebResource(String requestUrl) {
        String ext = FileHelper.getExtension(requestUrl, false);
        if (StringHelper.isEmpty(ext)) {
            return false;
        }
        return ArrayUtils.contains(WebUtils.WEB_STATIC_EXTS, ext);
    }

}
